﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Net;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using MyHttp.helper;

namespace MyHttp.http
{
    public class Request
    {
        public enum METHOD_ENUM { METHOD_GET, METHOD_POST, METHOD_PUT,
                METHOD_PATCH, METHOD_DELETE, METHOD_PURGE,
                METHOD_HEAD, METHOD_OPTIONS, METHOD_TRACE,
                METHOD_CONNECT, METHOD_UNKNOWN };

        public enum CONTENT_TYPE_ENUM { TYPE_FORM, TYPE_JSON, TYPE_XML,
                TYPE_RAW, TYPE_OTHER };

        public enum ACCEPT_TYPE_ENUM { TYPE_JSON, TYPE_XML, TYPE_TEXT,
                TYPE_OTHER };

        public static readonly char[] dolarSignDivisor, equalSignDivisor;

        static Request()
        {
            dolarSignDivisor = new char[] { '&' };
            equalSignDivisor = new char[] { '=' };
        }

        private METHOD_ENUM method;
        private CONTENT_TYPE_ENUM contentType;
        private List<ACCEPT_TYPE_ENUM> acceptTypes;
        private String rawBodyData;
        private String userAgent;
        private CookieCollection cookies;
        private String basePath;
        private Dictionary<String, Object> queryParameters, bodyParameters;

        public Request(HttpListenerRequest rawRequest)
        {
            parseRequestMethod(rawRequest);
            parseRequestContentType(rawRequest);
            parseRequestAcceptType(rawRequest);
            this.userAgent = rawRequest.UserAgent;
            this.cookies = rawRequest.Cookies;

            if (rawRequest.HasEntityBody)
            {
                StreamReader reader = new StreamReader(
                    rawRequest.InputStream, rawRequest.ContentEncoding);
                this.rawBodyData = reader.ReadToEnd();
            }

            this.basePath = rawRequest.Url.AbsolutePath;

            parseQueryParameters(rawRequest);
            parseBodyParameters(rawRequest);
        }

        private void parseRequestMethod(
            HttpListenerRequest rawRequest)
        {
            String method = rawRequest.HttpMethod.ToLower();
            if (method.CompareTo("get") == 0)
            {
                this.method = METHOD_ENUM.METHOD_GET;
            }
            else if (method.CompareTo("post") == 0)
            {
                this.method = METHOD_ENUM.METHOD_POST;
            }
            else if (method.CompareTo("put") == 0)
            {
                this.method = METHOD_ENUM.METHOD_PUT;
            }
            else if (method.CompareTo("patch") == 0)
            {
                this.method = METHOD_ENUM.METHOD_PATCH;
            }
            else if (method.CompareTo("delete") == 0)
            {
                this.method = METHOD_ENUM.METHOD_DELETE;
            }
            else if (method.CompareTo("purge") == 0)
            {
                this.method = METHOD_ENUM.METHOD_PURGE;
            }
            else if (method.CompareTo("head") == 0)
            {
                this.method = METHOD_ENUM.METHOD_HEAD;
            }
            else if (method.CompareTo("options") == 0)
            {
                this.method = METHOD_ENUM.METHOD_OPTIONS;
            }
            else if (method.CompareTo("trace") == 0)
            {
                this.method = METHOD_ENUM.METHOD_TRACE;
            }
            else if (method.CompareTo("connect") == 0)
            {
                this.method = METHOD_ENUM.METHOD_CONNECT;
            }
            else
            {
                this.method = METHOD_ENUM.METHOD_UNKNOWN;
            }
        }

        private void parseRequestContentType(
            HttpListenerRequest rawRequest)
        {
            String contentType = rawRequest.ContentType;
            if (null == rawRequest.ContentType)
            {
                this.contentType = CONTENT_TYPE_ENUM.TYPE_OTHER;
                return;
            }
            contentType = contentType.ToLower();
            if (contentType.StartsWith("application/x-www-form-urlencoded"))
            {
                this.contentType = CONTENT_TYPE_ENUM.TYPE_FORM;
            }
            else if (contentType.StartsWith("application/json"))
            {
                this.contentType = CONTENT_TYPE_ENUM.TYPE_JSON;
            }
            else if (contentType.StartsWith("text/xml") ||
                contentType.StartsWith("application/xml"))
            {
                this.contentType = CONTENT_TYPE_ENUM.TYPE_XML;
            }
            else if (contentType.StartsWith("multipart/form-data"))
            {
                this.contentType = CONTENT_TYPE_ENUM.TYPE_RAW;
            }
            else
            {
                this.contentType = CONTENT_TYPE_ENUM.TYPE_OTHER;
            }
        }

        private void parseRequestAcceptType(HttpListenerRequest rawRequest)
        {
            this.acceptTypes = new List<ACCEPT_TYPE_ENUM>();
            foreach (String str in rawRequest.AcceptTypes)
            {
                String accept = str.ToLower();
                if (accept.StartsWith(""))
                {

                }
            }
            this.acceptTypes.Add(ACCEPT_TYPE_ENUM.TYPE_OTHER);
        }

        private void parseQueryParameters(HttpListenerRequest rawRequest)
        {
            this.queryParameters = new Dictionary<String, Object>();
            String rawQuery = rawRequest.Url.Query;
            if (rawQuery.StartsWith("?"))
            {
                rawQuery = rawQuery.Substring(1);
            }
            String[] queries = rawRequest.Url.Query.Split(dolarSignDivisor);
            foreach (String query in queries)
            {
                String[] parts = query.Split(equalSignDivisor, 2);
                if (parts.Length > 1)
                {
                    DictionaryExtension.AddOrReplace(this.queryParameters,
                            parts[0], parts[1]);
                }
                else
                {
                    DictionaryExtension.AddOrReplace(this.queryParameters,
                            parts[0], null);
                }
            }
        }

        private void parseBodyParameters(HttpListenerRequest rawRequest)
        {
            this.bodyParameters = new Dictionary<String, Object>();
            if (null == this.rawBodyData) return;
            if (CONTENT_TYPE_ENUM.TYPE_FORM == this.contentType)
            {
                String[] queries = rawBodyData.Split(dolarSignDivisor);
                foreach (String query in queries)
                {
                    String[] parts = query.Split(equalSignDivisor, 2);
                    if (parts.Length > 1)
                    {
                        DictionaryExtension.AddOrReplace(this.bodyParameters,
                                parts[0], parts[1]);
                    }
                    else
                    {
                        DictionaryExtension.AddOrReplace(this.bodyParameters,
                                parts[0], null);
                    }
                }
            }
            else if (CONTENT_TYPE_ENUM.TYPE_JSON == this.contentType)
            {
                try
                {
                    Object result = JsonConvert.DeserializeObject(
                            this.rawBodyData);
                    if (result is JObject)
                    {
                        this.bodyParameters = (Dictionary<String, Object>)(
                                new MyJsonObject((JToken)result).Value);
                    }
                }
                catch (System.Exception ex) { }
            }
        }

        public String UserAgent
        {
            get
            {
                return this.userAgent;
            }
        }

        public CONTENT_TYPE_ENUM ContentType
        {
            get
            {
                return this.contentType;
            }
        }

        public CookieCollection Cookies
        {
            get
            {
                return this.cookies;
            }
        }

        public Object getQueryParameter(String name, Object defaultValue)
        {
            return DictionaryExtension.GetValue(queryParameters, name, defaultValue);
        }

        public Object getQueryParameter(String name)
        {
            return getQueryParameter(name, null);
        }

        public Object getBodyParameter(String name, Object defaultValue)
        {
            return DictionaryExtension.GetValue(bodyParameters, name, defaultValue);
        }

        public Object getBodyParameter(String name)
        {
            return getBodyParameter(name, null);
        }
    }
}
